// C++友元(friend)详解
// 友元是C++中一个独特的特性，它允许一个函数或类访问另一个类的私有和保护成员。友元打破了类的封装性，但在某些情况下是必要的。让我们深入了解友元的各个方面。

// 1. 友元的基本概念
// 友元声明是单向的、不可传递的特权授予：
//      单向性：如果A声明B为友元，B可以访问A的私有成员，但A不能访问B的私有成员
//      不可传递：如果A声明B为友元，B声明C为友元，C不会自动成为A的友元

// 2. 友元的三种形式
// 2.1 友元函数
// 允许非成员函数访问类的私有和保护成员：
class Box
{
private:
    double width;

public:
    Box(double w) : width(w) {}

    // 声明printWidth为友元函数
    friend void printWidth(const Box &box);
};

// 友元函数实现 - 可以访问Box的私有成员width
void printWidth(const Box &box)
{
    // 直接访问私有成员
    std::cout << "Width: " << box.width << std::endl;
}

// 2.2 友元类
// 允许另一个类的所有成员函数访问当前类的私有和保护成员：
class Box
{
private:
    double width;

public:
    Box(double w) : width(w) {}

    // 声明BoxManager为友元类
    friend class BoxManager;
};

class BoxManager
{
public:
    void modifyBox(Box &box, double newWidth)
    {
        // 可以直接访问Box的私有成员
        box.width = newWidth;
    }

    double getBoxWidth(const Box &box)
    {
        // 可以直接访问Box的私有成员
        return box.width;
    }
};

// 2.3 友元成员函数
// 只允许另一个类的特定成员函数访问当前类的私有和保护成员：
class Box; // 前向声明

class BoxManager
{
public:
    void modifyBox(Box &box, double newWidth);
    double getBoxWidth(const Box &box);
};

class Box
{
private:
    double width;

public:
    Box(double w) : width(w) {}

    // 只声明BoxManager::modifyBox为友元
    friend void BoxManager::modifyBox(Box &box, double newWidth);
    // getBoxWidth不是友元，不能访问私有成员
};

// 友元成员函数实现
void BoxManager::modifyBox(Box &box, double newWidth)
{
    box.width = newWidth; // 可以访问
}

double BoxManager::getBoxWidth(const Box &box)
{
    // 错误：不能访问Box::width
    // return box.width;

    // 必须使用公共接口
    return 0; // 假设有getter
}

// 3. 友元的声明位置
// 友元声明可以出现在类定义的任何部分（public、protected或private区域），不受访问说明符影响：
class Box
{
private:
    double width;

    // 友元声明在private区域，但这不影响友元的访问权限
    friend void printWidth(const Box &box);

public:
    Box(double w) : width(w) {}
};

// 4. 友元与模板
// 4.1 模板类的友元
template <typename T>
class Container
{
private:
    T data;

public:
    Container(const T &d) : data(d) {}

    // 普通函数作为友元
    friend void inspect(const Container<T> &c)
    {
        std::cout << "Data: " << c.data << std::endl;
    }

    // 模板函数作为友元
    template <typename U>
    friend void process(const Container<U> &c);
};

// 模板友元函数实现
template <typename U>
void process(const Container<U> &c)
{
    std::cout << "Processing: " << c.data << std::endl;
}

// 4.2 特定模板实例的友元
template <typename T>
class Box
{
private:
    T value;

public:
    Box(const T &v) : value(v) {}

    // 只有Container<int>是友元，Container<double>不是
    friend class Container<int>; // Container<int>的任何实例都可以访问Box<T>的私有成员
};

// 5. 友元的高级用法
// 5.1 内联友元函数
// 在类内定义友元函数，使其成为内联函数：
class Counter
{
private:
    int count;

public:
    Counter() : count(0) {}

    // 内联友元函数
    friend void reset(Counter &c)
    {
        c.count = 0;
    }
};

// 5.2 友元函数的前向声明
class Box; // 前向声明

// 友元函数声明
void printBox(const Box &box);

class Box
{
private:
    double width;

public:
    Box(double w) : width(w) {}

    friend void printBox(const Box &box);
};

// 友元函数实现
void printBox(const Box &box)
{
    std::cout << "Width: " << box.width << std::endl;
}

// 5.3 友元与运算符重载
class Complex
{
private:
    double real;
    double imag;

public:
    Complex(double r, double i) : real(r), imag(i) {}

    // 友元运算符重载
    friend Complex operator+(const Complex &lhs, const Complex &rhs)
    {
        return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
    }

    friend std::ostream &operator<<(std::ostream &os, const Complex &c)
    {
        return os << c.real << " + " << c.imag << "i";
    }
};

// 6. 友元的优缺点
// 优点
//      提高效率：直接访问私有成员，避免通过公共接口的间接调用
//      增强功能：允许非成员函数（如运算符重载）访问私有数据
//      提高灵活性：在保持封装的同时允许受控的外部访问
// 缺点
//      破坏封装：友元可以访问私有成员，降低了封装性
//      增加耦合：创建了类之间的依赖关系
//      可能导致维护问题：如果类的实现改变，可能需要修改友元

// 7. 友元的常见应用场景
// 7.1 运算符重载
// 如前所述，友元常用于实现对称的运算符重载。

// 7.2 辅助类
// 有时需要创建辅助类来管理主类的内部状态：
class StringData
{
private:
    char *data;
    size_t size;
    size_t capacity;

    // String可以直接管理StringData的内部
    friend class String;

    StringData(const char *s);
    ~StringData();
};

class String
{
private:
    StringData *data;

public:
    String(const char *s) : data(new StringData(s)) {}
    // ...
};

// 7.3 迭代器模式
// 容器类通常将其迭代器类声明为友元：
template <typename T>
class Vector
{
private:
    T *elements;
    size_t size;

    // 迭代器是友元
    friend class Iterator;

public:
    class Iterator
    {
    private:
        Vector<T> *vector;
        size_t index;

    public:
        T &operator*()
        {
            // 直接访问Vector的私有成员
            return vector->elements[index];
        }
        // ...
    };
    // ...
};

// 8. 友元的最佳实践
// 谨慎使用：只在必要时使用友元，不要过度使用
// 最小特权原则：尽量使用友元成员函数而非友元类
// 文档化：清晰记录友元关系及其目的
// 考虑替代方案：有时公共接口或保护成员可能是更好的选择
// 保持一致性：在整个代码库中一致地应用友元模式

// 9. 友元与设计模式
// 友元在某些设计模式中很有用：
// 观察者模式：Subject可能将Observer声明为友元
// 访问者模式：Element类可能将Visitor声明为友元
// 代理模式：Proxy可能是RealSubject的友元
// 桥接模式：Abstraction可能将Implementor声明为友元

// 10. 友元与C++标准库
// 标准库中友元的应用示例：

// std::basic_string和std::basic_stringbuf是相互的友元
// 容器类通常将其迭代器类声明为友元
// 智能指针类将相关的辅助类声明为友元
// 流类有友元运算符重载

// 总结
// 友元是C++中一个强大但需谨慎使用的特性。它允许在保持整体封装的同时，为特定函数或类提供受控的访问权限。虽然友元打破了严格的封装，但在某些情况下（如运算符重载、辅助类、迭代器等）是必要且有用的。理解友元的工作原理、适用场景和最佳实践，可以帮助您在设计C++类时做出更明智的决策。